package com.etouch.security.config.security;

import com.etouch.security.config.security.handler.FailureAuthenticationHandler;
import com.etouch.security.config.security.handler.SuccessAuthenticationHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;

/**
 * Spring Security配置类
 *
 * @author chenyunchang
 */
@Configuration
@EnableWebSecurity
//开启Security注解(使用接口上的注解来控制访问权限)
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private FailureAuthenticationHandler failureAuthenticationHandler;

    @Autowired
    private SuccessAuthenticationHandler successAuthenticationHandler;


    @Autowired
    private UserDetailsServiceImpl userDetailsService;


    /**
     * 注入身份管理器bean
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 密码加密器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        /**
         * BCryptPasswordEncoder：相同的密码明文每次生成的密文都不同，安全性更高
         */
        return new BCryptPasswordEncoder();
    }

    /**
     * 把userService 放入AuthenticationManagerBuilder 里
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(new BCryptPasswordEncoder());
    }


    /**
     * 表达式	说明
     * hasRole([role])	用户拥有制定的角色时返回true （Spring security默认会带有ROLE_前缀）
     * hasAnyRole([role1,role2])	用户拥有任意一个制定的角色时返回true
     * hasAuthority([authority])	等同于hasRole,但不会带有ROLE_前缀
     * asAnyAuthority([auth1,auth2])	等同于hasAnyRole
     * permitAll	永远返回true
     * denyAll	永远返回false
     * authentication	当前登录用户的authentication对象
     * fullAuthenticated	当前用户既不是anonymous也不是rememberMe用户时返回true
     * hasIpAddress('192.168.1.0/24'))	请求发送的IP匹配时返回true
     */
    /**
     * Http安全配置
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 禁用 csrf, 由于使用的是JWT，我们这里不需要csrf
        http.cors().and().csrf().disable();
        //配置自定义登陆路径
        http.formLogin()
                //登陆接口
                .loginProcessingUrl("/sys/login")
                //自定义登陆失败处理
                .failureHandler(failureAuthenticationHandler)
                //自定义登陆成功处理
                .successHandler(successAuthenticationHandler);
        //配置无需认证的访问路径
        http.authorizeRequests()
                // 跨域预检请求
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                // 登录URL
                .antMatchers("/sys/login").permitAll()
                // swagger
                .antMatchers("/swagger**/**").permitAll()
                .antMatchers("/webjars/**").permitAll()
                .antMatchers("/v2/**").permitAll()
                // 其他所有请求需要身份认证
                .anyRequest().authenticated();
        // 退出登录处理器
        http.logout()
                .logoutUrl("/sys/logout")
                .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler());
    }

    /**
     * 配置无需登陆就可以访问的路径
     *
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        //allow Swagger URL to be accessed without authentication
        web.ignoring().antMatchers(
                //swagger api json
                "/v2/api-docs",
                //用来获取支持的动作
                "/swagger-resources/configuration/ui",
                //用来获取api-docs的URI
                "/swagger-resources",
                //安全选项
                "/swagger-resources/configuration/security",
                "/swagger-ui.html",
                "/doc.html",
                "/css/**", "/js/**"
        );
    }
}

